﻿#include "User.h"

#include <fstream>
#include <iostream>

#include <boost/bind.hpp>
#include <boost/foreach.hpp>

#include "http_help.h"

namespace webui {

User::User( boost::asio::io_service& io, const callback_map_t& cb_map ) 
: sock_(io) 
, cb_map_(cb_map)
{}

User::~User()
{
	sock_.shutdown(boost::asio::ip::tcp::socket::shutdown_both);
	sock_.close();
}

void User::RunRecv()
{
	using namespace boost::asio;
	async_read_until(sock_, buf_in_, "\r\n",
		boost::bind(&this_t::OnRecv_, this, placeholders::error, placeholders::bytes_transferred));
}

void User::OnRecv_( const boost::system::error_code& err, std::size_t cb )
{
	if (!err)
	{
		using namespace http;
		
		std::istream i_s(&buf_in_);
		
		std::string method;
		std::string url;
		i_s>>method>>url;

		head_ = (method=="HEAD");
		if( !head_ && (method!="GET") )
			Reply_Error_(501); // метод не поддерживается
		
		std::string path = URLPath(url);
		if( !path.size() )
			path = "index.html";

		std::string query = URLQuery(url);

		callback_map_t::const_iterator it = cb_map_.find(path);
		if( it==cb_map_.end() )
		{
			if( query.size() )
				return Reply_Error_(400); // нет коллбека (503?), или запрос файла с передачей параметров
			
			std::ifstream ifs(path.c_str());
			if( ifs )
			{
				std::filebuf* ifbuf = ifs.rdbuf();
				int ifsize=ifbuf->pubseekoff (0,std::ios::end,std::ios::in);
				ifbuf->pubseekpos(0,std::ios::in);
				
				return Reply_(ifbuf,ifsize,Ext2Type(Path2Ext(path)));
			}
			else
				return Reply_Error_(404);
		}
		else
		{
			try
			{
				std::string reply = ((it->second)( Query(query) )).get_json();
				return Reply_(reply,reply.size(),"application/json");
			}
			catch(...)
			{
				return Reply_Error_(503);
			}			
		}
	}
	else
		delete this;
}

void User::Reply_Error_( int code )
{
	std::string text = "<b>Error:</b>&nbsp;"+http::Code2Str(code);
	Reply_(text,text.size(),"text/html",code);
}

template<typename Content>
void User::Reply_( Content& content, size_t size, const std::string type, int code )
{
	std::ostream os(&buf_out_);
	os	<< "HTTP/1.0 "<<code<<' '<<http::Code2Str(code)<<"\r\n"
		<< "Connection: close\r\n"
		<< "Content-Type: "<< type <<"\r\n"
		<< "Content-Length: " << size << "\r\n"
		<< "\r\n";
	if( !head_ )
		os << content;

	namespace ph = boost::asio::placeholders;
	boost::asio::async_write(sock_,buf_out_,
		boost::bind(&this_t::OnSent_, this, ph::error, ph::bytes_transferred) );
}

} // namespace webui
